# -*- coding: utf-8 -*-
#
# @Author: CPS
# @email: 373704015@qq.com
# @Date: 
# @Last Modified by: CPS
# @Last Modified time: 2021-04-15 03:32:09.447327
# @file_path "D:\CPS\IDE\JS_SublmieText\Data\Packages\CPS\core"
# @Filename "comments_creator.py"
# @Description: 功能描述
#
import re

class ParamsParser():
    def __init__(self, reg_obj):
        self.result = False
        self.line_str = ""
        self.reg_obj = reg_obj
        self.string = []
        self.int = []
        self.float = []
        self.param = []
        self.type = ""
        self.name = ''
        self.output_str = ""
        self.old_comment = {
            "description":"",
            "params":{}
        }

        self.old_comment_list = []

        self.default_tmpl={
            "insert_alignment":True,
            "insert_alignment_indent":"  ",
            "preferred_comments_context":{
              "Description":'Description\n',
              "param":"@param {name}:{type} paramDescription",
              "returns":": returns {type} returnsDescription"
            },
            "preferred_comments_style": [
              "\"\"\"",
              "",
              "\"\"\"",
            ]
        }

    def __str__(self):
        return self.output_str

    def set_old_comments(self, comments_str):
        ree = re.compile('\n')
        self.old_comment_list = ree.split(comments_str)
        self.old_comment["description"] = self.extract_description('description')

        return self

    def get_description(self, tmpl):
        description = self.extract_description('description')
        res = tmpl.format(description=description) + '\n'
        return res

    """
    : Description {description}
    :
    : param  self:{type}      {description}
    : param  key_word:{type}  {description}
    :
    : returns {} {description}
    :
    """
    def extract_description(self, key_word):
        default = "{description}"
        reg = re.compile(r'\s+')
        comment_list = self.old_comment_list
        for each in comment_list:
            res = reg.split(each)
            if len(res)>0:
                for index, _each in enumerate(res):
                    if _each.lower().find(key_word.lower()) > -1:
                        ret = ''.join(res[-1])
                        return ret
                        break
        return default

    """
    : Description 分析函数参数
    :
    : param  self:{type}      213123
    : param  line_str:{type}  ccadsfasfsadf
    :
    : returns {} {description}
    :
    """
    def match_line(self, line_str):
        self.line_str = line_str
        res = self.reg_obj['func'].findall(line_str)

        if len(res)==1 and len(res[0]) >= 4:

            # 获取缩进
            indent_len = line_str.find(res[0][0])
            if indent_len > 0:
                line_start_indent = ' ' * indent_len
            else:
                line_start_indent = self.line_start_indent(res[0][0])

            self.result = {
                # "line_start_indent":self.line_start_indent(res[0][0]),
                "line_start_indent":line_start_indent,
                "type":self.get_type(res[0][1]),
                "name":self.get_name(res[0][2]),
                "params_obj":self.get_params(res[0][3]),
            }
            return True
        return False

    """
    : Description {description}
    :
    : param  self:{type}  {description}
    : param  tmpl:{type}  {description}
    :
    : returns {} {description}
    :
    """
    def format_by_tmpl(self, tmpl):
        # 处理 description 行
        description = self.get_description(tmpl['preferred_comments_context']['Description'])

        # 处理 returns 行
        returns = tmpl['preferred_comments_context']['returns'] + '\n'

        # 处理 param 行
        params_alignment = tmpl.get('params_alignment'," ")

        if not self.result: return self

        params = self.comment_params(self.result['params_obj'], tmpl['preferred_comments_context']['param'], params_alignment)

        # 合并所有内容
        # contexts_list = [description, *params, returns] # py3.8语法
        contexts_list = [description]
        contexts_list.extend(params)
        contexts_list.append(returns)

        # 添加 前缀 和 \n 结束符
        comment_prefix = tmpl['preferred_comments_style'][1]
        contexts_list = self.add_comment_prefix(comment_prefix, contexts_list)

        # 每行注释添加缩进
        line_indent = self.result['line_start_indent']
        self.line_indent = line_indent
        output_str = self.add_line_indent(line_indent, contexts_list)

        # 合并所有注释
        begin = line_indent + tmpl['preferred_comments_style'][0] + '\n'
        end = line_indent + tmpl['preferred_comments_style'][-1] + '\n'
        self.output_str = begin + ''.join(output_str) + end

        return self

    """
    : Description {description}
    :
    : param  self:{type}         {description}
    : param  params_obj:{type}   {description}
    : param  tmpl:{type}         {description}
    : param  alignment:{string}  {description}
    :
    : returns {} {description}
    :
    """
    def comment_params(self, params_obj, tmpl, alignment=""):
        params = []
        for each_param in params_obj:
            param = tmpl.format(
                name=each_param['name'],
                type='{'+each_param['type']+'}',
                description = self.extract_description(each_param['name'])
                )
            params.append(param)

        if alignment:
            params = self.insert_alignment(params, alignment)


        return [each + '\n' for each in params]

    def set_params_reg(self, key, reg):
        if key in self.reg:
            self.reg_obj[key] = re.compile(reg)

    @staticmethod
    def add_line_indent(indent, contexts_list):
        return [indent + each for each in contexts_list]

    @staticmethod
    def add_comment_prefix(comment_prefix, contexts_list):
        # 重新根据 \n 符号，重新分配每行
        constext_str = ''.join(contexts_list)
        contexts_list = constext_str.split('\n')
        return [comment_prefix + each + '\n' for each in contexts_list]

    """
    : Description {description}
    :
    : param-param_list:{type}-{description}
    : param-indent:{type}    -{description}
    :
    : returns {} {description}
    :
    """
    @staticmethod
    def insert_alignment(param_list, indent):
        res = []
        for each_param in param_list:
            # 默认模版使用空格分隔
            each_list = each_param.split(' ')

            # 获取每行的元素个数
            index = [e for e in range(len(each_list))]
            for i,each_index in enumerate(index):
                # 获取同一列最大的字符长度
                max_len = max([len(each_param.split(' ')[each_index]) for each_param in param_list])
                each_len = len(each_list[each_index])

                if each_len < max_len:
                    ex = (' ' * (max_len - each_len))
                    each_list[each_index] = each_list[each_index] + ex

            res.append(indent.join(each_list))
        return res

    @staticmethod
    def get_name(name_str):
        return str(name_str)

    def line_start_indent(self, indent_str, indent=' '):
        res = ''
        str_len = len(indent_str)
        if str_len > 0:
            res = indent * str_len
        return res

    def get_type(self, type_name):
        return str(type_name).strip()

    def get_params(self, params_str):
        res_list = []

        if not params_str or len(params_str) == 0:
            return res_list

        params_str = params_str.replace(' ',"").strip()
        params_list = params_str.split(',')
        raw_list = {
            "string" :self.extract_string(params_list),
            "int" : self.extract_int(params_list),
            "float" : self.extract_float(params_list),
            "params":self.extract_params(params_list),
        }

        for each in raw_list:
            res_list += raw_list[each]

        return sorted(res_list, key=lambda item:item['id'])

    def extract_params(self, params_list):
        result = []
        reg = self.reg_obj["params"]
        for index,each_param in enumerate(params_list):
            res = reg.findall(each_param)
            if len(res)>0:
                result.append({ "id":index, "type":"type", "name":each_param })
        return result

    def extract_string(self, params_list):
        result = []
        reg = self.reg_obj["string"]
        for index,each_param in enumerate(params_list):
            res = reg.findall(each_param)
            if len(res)>0:
                result.append({ "id":index, "type":"string", "name":res[0][0], "context":res[0][2] })
        return result

    def extract_int(self, params_list):
        result = []
        reg = self.reg_obj["int"]
        for index,each_param in enumerate(params_list):
            res = reg.findall(each_param)
            if len(res)>0:
                result.append({ "id":index, "type":"int", "name":res[0][0], "context":res[0][1] })
        return result

    def extract_float(self, params_list):
        result = []
        reg = self.reg_obj["float"]
        for index,each_param in enumerate(params_list):
            res = reg.findall(each_param)
            if len(res)>0:
                result.append({ "id":index, "type":"float", "name":res[0][0], "context":res[0][1] })
        return result

"""
: Description
:
: param  ParamsParser:{type}  paramDescription
:
: returns {} returnsDescription
:
"""
class Python(ParamsParser):
    def __init__(self):
        super(Python, self).__init__({
            # 查找 class 和 def，分辨函数
            "func":re.compile(r'^(\s*)?(class|def)\s(\w+)\((.*)\):'),
            "params":re.compile(r'^(\w+|\*\*\w+|\*\w+)$'),
            "string":re.compile(r'(\w+)\=(\'|\")(.*)(\'|\")'),
            "int":re.compile(r'(\w+)\=([1-9]\d*$|-[1-9]\d*$|0)'),
            "float":re.compile(r'(\w+)\=([1-9]\d*\.\d*|0\.\d*[1-9]\d*$|-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$)'),
        })


"""
: Description js的解释引擎
:
: param  ParamsParser:{type}  paramDescription
:
: returns {} returnsDescription
:
"""
class JavaScript(ParamsParser):
    def __init__(self):
        super(JavaScript, self).__init__({
            # func 匹配公式存在缺憾，如果缩进为4格，他只能匹配为3
            # "func":re.compile(r'(\s+)?(export\sfunction|async|async\sfunction|function)?\s?(\w+)\((.*)\)\s?[=>]?\{'),
            # 0 缩进 | 1 函数类型 | 2 函数名称 | 3 函数左括号 | 4 函数参数 | 5 函数右括号
            "func":[
                        {
                            "reg":re.compile(r'(\s+?)(export\sfunction|async|async\sfunction|function)?\s?(\w+)\s?(\(\{|\()\s?([^\{].*[^\}])?\s?(\}\)|\))\s?[=>]?\{'),
                            "indent":0,# 在匹配式的位置
                            "type":1,# 在匹配式的位置
                            "name":2,# 在匹配式的位置
                            "params":4# 在匹配式的位置
                        },
                        {
                            "reg":re.compile(r'(\s+?)(class)\s+(.*[^\{])\{'),
                            "indent":0,
                            "type":1,
                            "name":2,
                        }
                    ],
            "params":re.compile(r'^(\w+|\*\*\w+|\*\w+)$'),
            "string":re.compile(r'(\w+)\=(\'|\")(.*)(\'|\")'),
            "int":re.compile(r'(\w+)\=([1-9]\d*$|-[1-9]\d*$|0)'),
            "float":re.compile(r'(\w+)\=([1-9]\d*\.\d*|0\.\d*[1-9]\d*$|-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$)'),
        })

    def match_line(self, line_str):
        self.line_str = line_str

        res = None
        for each_reg in self.reg_obj['func']:
            res = each_reg['reg'].findall(line_str)
            if(len(res)==1):
                params_obj=""

                if 'params' in each_reg:
                    params_obj = res[0][each_reg['params']]

                # 获取缩进和其他参数
                self.result = {
                    "line_start_indent":self.line_start_indent(res[0][each_reg['indent']]),
                    "type":self.get_type(res[0][each_reg['type']]),
                    "name":self.get_name(res[0][each_reg['name']]),
                    "params_obj":self.get_params(params_obj),
                }

                return True
        return False

PARSER={
    'python':Python,
    'vue':JavaScript,
    'javascript':JavaScript,
}

if ( __name__ == "__main__"):
    tmpl={
        "params_alignment":"  ",
          "preferred_comments_context":{
            "Description":"Description - {description}\n",
            "param":"@param {type} {name} - {description}",
            "returns":"\n@returns {<type>} - {description}"
          },
        "preferred_comments_style": [
            "/**",
            " * ",
            " */"
        ]
    }

    test1 = r'    class TesttCommentsCreatorCommand( target, age=5, bill=-5.1, flag=None, default_name="ccvbb", *key,**rt ):\n'
    test2 = r"        def test( target, age=5, bill=-5.1, flag=None, default_name='ccvbb', *key,**rt ):\n"
    test3 = r'        def get_pre_line_point(selfff, curt_line, reg):'
    test4 = r'function format(tar, options) {'
    test5 = r'async format(tar, options) {'
    test6 = r'async function format(tar, options) {'
    test6 = r'export function format(tar, options) {'
    test7 = r'    async upload(target, age=5, bill=-5.1, flag=None) {'
    test8 = r'    getPointsResizePosition(posData, ratio) {'
    test9 = r'  onLaunch: function (params) {'
    test10 = r'uni.\$on("init", options => {'
    test11 = r'    class Circle {'
    test12 = r'    drawDot({ filleStyle = "#00ff00", size = 14, x = 0, y = 0 }) {'
    test13 = r'    drawDot() {'
    test14 = r'  class Vector2d{}'

    old_comment = '''        """
        : Description 查找当前行是否存在reg内容，如果存在，则返回当前行的上一行
        :
        : param  self:{type}       - paramDescription1
        : param  curt_line:{type}  - 123123123
        :
        : returns {int} 如果匹配到reg则返回上一行结尾位置，否则返回当前行结尾位置
        :
        """'''

    js = PARSER['javascript']()

    print(js.match_line(test14))

    js.set_old_comments(old_comment)
    js.format_by_tmpl(tmpl)

    print(js)
